:-module(de,[mod1/3,incr/4,list_locate/4,list_locate1/4,
			    num2bin/3,bin2num/2,firstn/2,range/4,
			    print_list/1,print_list/2
			  ]).
%% 修正同余式，余数从 1 ~ M
mod1(A,B,C):-
	X is A mod B,
	(X = 0 -> C = B; C = X).

%% 构建一个新 DICT，用 Key-Value 对创建
new(Var,KV):-
	dict_create(V,de,KV),
	integer(V.count), abs(V.count) > 1,
	(
	 var(V.value) -> 	%如果 value 属性是自由变量
	 Var = V, firstn(V.count,V.value); %那么遍历
	 integer(V.value) ->
	 NV is V.value mod V.count, Var = V.put(value,NV); % 如果传递进来的是整数，转换到最小同余类
	 set_value_by_name(Var,V)		   %否则调用 set_value_by_name 设置
	).

%% 设置值名称列表中查询序号作为 value 属性的值
set_value_by_name(Var,Dict):-
	atom_concat(Dict.type,'_items',X),
	call(X,List),
	nth0(Val,List,Dict.value),!
	Var = Dict.put(value,Val).

%% 基于同余式加
M.add(N):=R:-
	V is (M.value + N) mod M.count,
	R = M.put(value,V).

%% 基于同余式，用一个数减去自己的 value 属性
M.rsub(N):=R:-
	V is (N - M.value) mod M.count,
	R = M.put(value,V).

%% 获取 DICT 对应的中文符号
M.name():=R:-
	atom_concat(M.type,'_items',X),
	call(X,List),
	nth0(M.value,List,R).

%% 根据类型和索引获取列表值
_.get_item(Type,Index):= R :-
	integer(Index),
	call(Type,List),
	nth0(Index,List,R), !.
%% 和上一句的区别，这里的索引是一个列表，所以结果也是一个列表
_.get_item(Type,Index):= R :-
	is_list(Index),
	call(Type,List),
	list_at(List,Index,R).
%% 基于同余式的遍历所有满足 A + B = C (mod Base)
incr(Base,A,B,C):-
	N is Base - 1,
	maplist(between(0,N),[A,B]),
	C is (A + B) mod Base.

%% 根据多个索引查找对应的值
%% list_at(+列表,+索引列表,-元素列表)
%% list_at(+列表,+索引列表,元素初始列表,-元素列表)
list_at(A,B,C):-
	list_at(A,B,[],C).
list_at(_,[],ET,ET):-
	!.
list_at(List,[Head|Tail],ET,Ele):-
	nth0(Head,List,HE),
	append(ET,[HE],NewET),
	list_at(List,Tail,NewET,Ele).
%% 函数形式
_.list_at(List,Index):=R:-
	integer(Index),
	nth0(Index,List,R).
_.list_at(List,Index):=R:-
	is_list(Index),
	list_at(List,Index,R).
_.list_index(List,Ele):= R :-
	nth0(R,List,Ele).

%% 查找在有序列表中不超过指定数值的最大位置的索引值,并返回两者之差
%% 二分查找
%% list_locate(+List,+Num,-Index,-Diff)
list_locate([],_,_,_):-
	fail, !.
list_locate([H],N,0,D):-
	D is N - H,
	D >= 0, !.
list_locate(List,Num,Index,Diff):-
	append(Prefix,Postfix,List),
	length(Prefix,L1),
	length(Postfix,L2),
	L2 - L1 < 2, !,
	last(Prefix,E1),
	Postfix = [E2|_],
	(
	  E1 > Num -> list_locate(Prefix,Num,Index,Diff);
	  E2 < Num -> list_locate(Postfix,Num,I1,Diff), Index is L1 + I1;
	  Index is L1 - 1, Diff is Num - E1
	).

%% 同上,但是查找的不是在有序数组中的位置,而是查找最多能减去前几项
%% 结果是1Base的
list_locate1(List,Num,Index,Diff):-
	findall(Sum,(
		          append(L1,_,List),
		          sum_list(L1,Sum)
		        ),ListOfSum),
	list_locate(ListOfSum,Num,Index,Diff).

%% 数到二进制数组的相互转换
num2bin(_,0,Bits,Bits):-!.
num2bin(Num,Len,Bits,Tail):-
	Len > 0,
	B is Num /\ 1,
	N is Num >> 1,
	L is Len - 1,
	NewT = [B|Tail],
	num2bin(N,L,Bits,NewT).
num2bin(Num,Len,Bits):-
	num2bin(Num,Len,Bits,[]).

bin2num([],A,A):-!.
bin2num(Bits,Num,Tail):-
	Bits = [H|T],
	NewT is H + (Tail << 1),
	bin2num(T,Num,NewT).
bin2num(Bits,Num):-
	bin2num(Bits,Num,0).

%% 自定义打印,如果是类属元素,打印名字,否则调用write
aprint(N):-
	is_dict(N,de) -> write(N.name());
	write(N).
print_list([H|T],Split):-
	(is_list(H)->print_list(H,Split);aprint(H)),
	(
	  T = [] -> !;
	  aprint(Split),print_list(T,Split)
	 ).

print_list(L):- print_list(L,' ').

%% 返回前 N 个自然数 [0,N-1]
firstn(N,V):-
    P is N - 1,
    between(0,P,V).

%% range(初值，边界，步长，)
range(_,_,0,_):-
	fail,!.
range(Start,End,Step,N):-
	A is floor((End - Start) / Step) ,
	between(0,A,B),
	N is Start + Step * B.

%% 辅助函数,基本元素的转换

%% 取得花甲子的天干和地支
M.gan():= R :-
	get_dict(type,M,hj),
	hj:split(M,R,_).

M.zhi():= R :-
	get_dict(type,M,hj),
	hj:split(M,_,R).

%% 调用 wuxing 谓词，取得 DICT 的五行对象
M.wuxing():= R :-
	A =.. [:, M.type, wuxing()],
	call(A,M,R).

